﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Media.Imaging;

namespace ELTE.TFEG.ImageTransform.Hough
{
    public class HoughLine : IComparable
    {

        public double ThetaAngle { get; set; }

        public int Radius { get; set; }

        public int Intensity { get; set; }

        public double RelativeIntensity { get; set; }

        public int CompareTo(object value)
        {
            HoughLine valueAsHoughLine = value as HoughLine;
            if (valueAsHoughLine == null)
            {
                return int.MinValue;
            }
            else
            {
                return Intensity.CompareTo(valueAsHoughLine.Intensity).Negate();
            }
        }

        public Tuple<Point, Point> GetLineEndPoints(Bitmap image)
        {
            // get line's radius and theta values
            int r = this.Radius;
            double theta = this.ThetaAngle;

            //if the lower side of the image -> negate
            if (r < 0)
            {
                theta += 180;
                r = r.Negate();
            }

            theta = theta.ToRadian();

            Point imageCenter = new Point { X = image.Width / 2, Y = image.Height / 2 };

            double x0 = 0, x1 = 0, y0 = 0, y1 = 0;

            if (this.ThetaAngle != 0)
            {
                x0 = imageCenter.X.Negate(); // will be the most left point
                x1 = imageCenter.X;  // will be the most right point
                y0 = (-Math.Cos(theta) * x0 + r) / Math.Sin(theta);
                y1 = (-Math.Cos(theta) * x1 + r) / Math.Sin(theta);
            }
            else
            {
                x0 = this.Radius;
                x1 = this.Radius;

                y0 = imageCenter.Y;
                y1 = imageCenter.Y.Negate();
            }

            Point p1 = new Point(Convert.ToInt32(x0) + imageCenter.X, imageCenter.Y - Convert.ToInt32(y0));
            Point p2 = new Point(Convert.ToInt32(x1) + imageCenter.X, imageCenter.Y - Convert.ToInt32(y1));
            return Tuple.Create<Point, Point>(p1, p2);
        }

        private List<Point> GetPointsOfLine(Tuple<Point, Point> endPoints)
        {
            List<Point> result = new List<Point>();
            try
            {
                // y = a*x + b
                // endPoints.Item2.Y = a* endPoints.Item2.X + b
                // endPoints.Item1.Y = a* endPoints.Item1.X + b
                //from here
                if (endPoints.Item1.X == endPoints.Item2.X)
                {
                    //it is a vertical line
                    for (int i = Math.Min(endPoints.Item1.Y, endPoints.Item2.Y); i < Math.Max(endPoints.Item1.Y, endPoints.Item2.Y); i++)
                    {
                        result.Add(new Point(endPoints.Item1.X, i));
                    }
                }
                else
                {
                    if (endPoints.Item2.Y == endPoints.Item1.Y)
                    {
                        //it is a horizontal linne
                        for (int i = Math.Min(endPoints.Item1.X, endPoints.Item2.X); i < Math.Max(endPoints.Item1.X, endPoints.Item2.X); i++)
                        {
                            result.Add(new Point(i, endPoints.Item1.Y));
                        }
                    }
                    else
                    {
                        int b = 0;
                        int a = 0;
                        if (endPoints.Item1.X == 0)
                        {
                            b = endPoints.Item1.Y;
                            if (endPoints.Item2.X != 0)
                            {
                                a = (endPoints.Item2.Y - b) / endPoints.Item2.X;
                            }
                        }
                        else
                        {
                            if (endPoints.Item2.X == 0)
                            {
                                b = endPoints.Item2.Y;
                                if (endPoints.Item1.X != 0)
                                {
                                    a = (endPoints.Item1.Y - b) / endPoints.Item1.X;
                                }
                            }
                            else
                            {
                                // endPoints.Item2.Y = a* endPoints.Item2.X + b
                                // endPoints.Item1.Y = a* endPoints.Item1.X + b
                                // therefore
                                // b == endPoints.Item2.Y - a* endPoints.Item2.X
                                // and
                                // b == endPoints.Item1.Y - a* endPoints.Item1.Y
                                // so
                                // endPoints.Item1.Y - a* endPoints.Item1.X == endPoints.Item2.Y - a* endPoints.Item2.X
                                // endPoints.Item1.Y - endPoints.Item2.Y == a*(endPoints.Item1.X - endPoints.Item2.X)
                                // a == endPoints.Item1.Y - endPoints.Item2.Y / (endPoints.Item1.Y - endPoints.Item2.X)
                                a = endPoints.Item1.Y - endPoints.Item2.Y / (endPoints.Item1.Y - endPoints.Item2.X);
                                b = endPoints.Item1.Y - a * endPoints.Item1.Y;
                                
                            }
                        }
                        int minX = Math.Min(endPoints.Item1.X, endPoints.Item2.X);
                        int maxX = Math.Max(endPoints.Item1.X, endPoints.Item2.X);
                        for (int x = minX; x <  maxX; x++)
                        {
                            result.Add(new Point(x, a * x + b));
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                //swallow exception
            }
            return result.Where(p => p.X > 0 && p.Y > 0).ToList();
        }



        public Bitmap TryDrawOnImage(Bitmap image, Pen pen)
        {
            try
            {
                Tuple<Point, Point> endPoints = GetLineEndPoints(image);
                using (Graphics g = Graphics.FromImage(image))
                {
                    g.DrawLine(pen,
                        endPoints.Item1, endPoints.Item2
                        );
                }
                return image;
            }
            catch (Exception ex)
            {
                return image;
            }
        }

        public Bitmap TryDrawLineSegment(Bitmap image, Pen pen)
        {
            try
            {
                List<Point> LinePointsGot = GetPointsOfLine(GetLineEndPoints(image));
                    
                LinePointsGot
                    .Where(p => p.X < image.Width && p.Y < image.Height)
                    .ToList()
                    .ForEach(p => image.SetPixel(p.X, p.Y, pen.Color));

                return image;
            }
            catch (Exception ex)
            {
                return image;
            }
        }
    }
}
